package com.rigsofrods.truckeditor.transforms3D;

public class Camera {
	public static final int ISO_VIEW = 0;
	public static final int FRONT_VIEW = 1;
	public static final int BACK_VIEW = 2;
	public static final int LEFT_VIEW = 3;	
	public static final int RIGHT_VIEW = 4;
	public static final int TOP_VIEW = 5;
	public static final int BOTTOM_VIEW = 6;
	private static final int CUSTOM_VIEW = 7;

	private static final String[] PRESET_VIEWS_NAMES = {
		"iso", "front", "back", "left", "right", "top", "bottom", "custom"};//FIXME: Camera names and directions
	
	private static final Vec3D[] PRESET_EYEVECTORS = {
		new Vec3D(-1, -1, -1).normalized(),
		new Vec3D(0, 0, 1),
		new Vec3D(0, 0, -1),
		new Vec3D(1, 0, 0),
		new Vec3D(-1, 0, 0),
		new Vec3D(0, 1, 0),
		new Vec3D(0, -1, 0),
	};
	
	public enum PresetView{
		ISO_VIEW("Iso", 1.25 * Math.PI, -0.25 * Math.PI),
		FRONT_VIEW("Front", Math.PI, 0),
		BACK_VIEW("Back", 0, 0),
		LEFT_VIEW("Left", - 0.5 * Math.PI, 0),
		RIGHT_VIEW("Right", 0.5 * Math.PI, 0),
		TOP_VIEW("Top", Math.PI, - 0.5 * Math.PI),
		BOTTOM_VIEW("Bottom", 0, 0.5 * Math.PI);
		
		String name;
		double azimuth;
		double zenith;
		
		private PresetView(String name, double azimuth, double zenith) {
			this.name = name;
			this.azimuth = azimuth;
			this.zenith = zenith;
		}
	}
	
	private static final Vec3D UP = new Vec3D(0, 1, 0);
	private static final double SNAP_LIMIT = 0.999;

	/*
	public static final Camera3rd PERSPECTIVE_CAM = new Camera3rd(-3/(double)4*Math.PI, -1/(double)4*Math.PI, new Vec3D(0, 0, 0), 1200);//perspective 0;
	public static final Camera3rd FRONT_CAM = new Camera3rd(Math.PI/2, 0.0f, new Vec3D(0, -400, 0), 700);//front 1;
	public static final Camera3rd BACK_CAM = new Camera3rd(-Math.PI/2, 0.0f, new Vec3D(0, 400, 0), 700);//back 2;
	public static final Camera3rd LEFT_CAM = new Camera3rd(0.0, 0.0f, new Vec3D(-400, 0, 0), 700);//left 3;
	public static final Camera3rd RIGHT_CAM = new Camera3rd(-Math.PI, 0.0f, new Vec3D(400, 0, 0), 700);//right 4;
	public static final Camera3rd TOP_CAM = new Camera3rd(Math.PI/2, -1/(double)2*Math.PI, new Vec3D(0, 0, 400), 700);//top 5;
	public static final Camera3rd BOTTOM_CAM = new Camera3rd(-Math.PI/2, 1/(double)2*Math.PI, new Vec3D(0, 0, 400), 700);//bottom 6;
	*/
	private PresetView activeView = PresetView.ISO_VIEW;	
	private boolean firstPerson = false;

	private Vec3D eye, at, dir, up, left, pos;
	private double azimuth, radius, zenith;
	
	private Mat4 view;
	private boolean enableSnapping = true;
		
	public Camera() {
		pos = at = new Vec3D();
		firstPerson = false;
		azimuth = zenith = 0.0f;
		radius = 3.0f;
		computeMatrix();
	}
	
	/**
	 * @param PresetView
	 * Creates a preset camera
	 */
	public Camera(PresetView view) {
		activeView = view;
		pos = at = new Vec3D();
		firstPerson = false;
		azimuth = view.azimuth;
		zenith = view.zenith;
		radius = 3.0f;
		computeMatrix();
	}
	
	public Camera(double azimuth, double zenith, Vec3D pos, double radius) {
		at = new Vec3D();
		firstPerson = false;
		computeMatrix();
	}

	private void computeMatrix() {
		//TODO: Parse camera from truck file to get correct base
		dir = new Vec3D((Math.sin(azimuth) * Math.cos(zenith)),
						Math.sin(zenith),
						(Math.cos(azimuth) * Math.cos(zenith)));
		up = new Vec3D(Math.sin(azimuth) * Math.cos(zenith + Math.PI / 2), 
						Math.sin(zenith + Math.PI / 2),
						Math.cos(azimuth) * Math.cos(zenith + Math.PI / 2));
		if(enableSnapping){
			Vec3D dom = dir.dominantAxis();
			if (dir.dot(dom) > SNAP_LIMIT) {
				dir = dom;
				up = up.dominantAxis();
			}
		}
		if (firstPerson) {
			eye = new Vec3D(pos);
			view = new Mat4ViewRH(pos, dir.times(radius), up);
		} else {
			eye = pos.plus(dir.times(-1 * radius));			
			view = new Mat4ViewRH(eye, dir.times(radius), up);
		}
	}

	public void addAzimuth(double ang) {
		azimuth += ang;
		computeMatrix();
	}

	public void addZenith(double ang) {
		zenith += ang;
		computeMatrix();
	}
	
	public void lookAt(Vec3D target){
		//TODO: lookAt
		at = target;
		computeMatrix();
	}
	
	/*public void addRadius(double dist) {//FIXME: addRadius
		if (radius + dist < 0.1f)
			return;
		radius += dist;
		eye.add(dir.mul(dist));
		computeMatrix();
	}*/
	
	public void zoom(int step){
		if(firstPerson) forward(step);//TODO: 1st person camera zoom
		//dir = at.minus(pos);
		assert(!dir.isZero());
		radius *= Math.pow(1.01, step);
		//System.out.println("radius: "+radius+" | eye: "+eye+" | at: "+at+" | dir: "+dir);
		//TODO if(dir.lengthSqr() <= radius*radius) return;
		//dir = dir.mul(radius);
		//pos = at.minus(dir);
		computeMatrix();
	}

	public void backward(double speed) {
		forward((-1) * speed);
	}

	public void forward(double speed) {
		//TODO Rather add speed * normalized eyeVector?
		//pos = pos.add(getEyeVector().normalized().mul(speed));
		pos = pos.plus(new Vec3D(
				(double) (Math.cos(azimuth) * Math.cos(zenith)), (double) (Math
						.sin(azimuth) * Math.cos(zenith)), (double) Math
						.sin(zenith)).times(speed));
		computeMatrix();
	}

	public void left(double speed) {
		right((-1) * speed);
	}

	public void right(double speed) {
		pos = pos.plus(new Vec3D((double) Math.cos(azimuth - Math.PI / 2),
				(double) Math.sin(azimuth - Math.PI / 2), 0.0f).times(speed));
		computeMatrix();

	}

	public void down(double speed) {
		pos.z -= speed;
		computeMatrix();
	}

	public void up(double speed) {
		pos.z += speed;
		computeMatrix();
	}

	public void move(Vec3D dir) {
		pos.add(dir);
		at.add(dir);
		computeMatrix();
	}

	public boolean isFirstPerson() {
		return firstPerson;
	}

	public void setFirstPerson(boolean fp) {
		firstPerson = fp;
		computeMatrix();
	}

	public Vec3D getEye() {
		return eye;
	}

	public Vec3D getEyeVector() {
		return dir;
	}

	public Vec3D getTarget() {
		return at;
	}

	public Mat4 getViewMatrix() {
		return view;
	}
	
	public double getAzimuth() {
		return azimuth;
	}
	public double getZenith() {
		return zenith;
	}

	public double getRadius() {
		return radius;
	}

	public String getViewName(){
		return activeView.name;//FIXME: camera name
	}

	public Vec3D getPos() {
		return pos;
	}

}
